﻿using Newtonsoft.Json;
using PropertyChanged;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Runtime.CompilerServices;
using System.Xml.Linq;
using VHslComm.Secs.Types;
using VMathNet.Numerics;
using static OpenCvSharp.Stitcher;

namespace VIA.SiMoJi.Domain.Electronics
{

    public interface INGTrayItem : ITrayItem
    {
        void Put();
        (double PointX, double PointY) GetPutPoint(double baseX, double baseY);
    }

    public interface IFeedTrayItem : ITrayItem
    {
    }

    public interface ITrayItem
    {
        /// <summary>
        /// 是否有料
        /// </summary>
        bool IsExist { get; }
        int Index { get; }
        int Row { get; }
        int Col { get; }
        (double OffsetX, double OffSetY) GetOffsetPoint();
    }

    public class TrayItem : ITrayItem
    {
        protected double _colDistance;
        protected double _rowDistance;
        public virtual int Index { get; set; }
        public int Row { get; protected set; }
        public int Col { get; protected set; }
        public bool IsExist { get; set; }
        public TrayItem() { }
        public TrayItem(int row, int col, double rowDist, double colDist)
        {
            Row = row;
            Col = col;
            _rowDistance = rowDist;
            _colDistance = colDist;
        }

        public void SetDistance(double rowDist, double colDist)
        {
            _rowDistance = rowDist;
            _colDistance = colDist;
        }
        /// <summary>
        /// 根据所在行列获取X轴和Y轴坐标偏移
        /// </summary>
        /// <param name="row">行数,从1开始算起</param>
        /// <param name="col">列数,从1开始算起</param>
        /// <returns></returns>
        public (double OffsetX, double OffSetY) GetOffsetPoint()
        {
            double offsetx = (Col - 1) * _colDistance;
            double offsety = (Row - 1) * _rowDistance;
            return (offsetx, offsety);
        }
    }

    [AddINotifyPropertyChangedInterface]
    public class NGTray : Tray
    {
        public static NGTray Instance { get; private set; } = Reload();
        NGTrayItem[] _items;
        private NGTrayStatus status;
        //public double RowDist { get; set; }
        //public double ColDist { get; set; }

        public NGTrayRow[] NGTrayRows { get; set; }
        public NGTrayStatus Status { get => status; protected set { status = value; OnNGTrayStatusChanged(value); } }

        public event EventHandler<NGTrayStatus> NGTrayStatusChanged;
        public event EventHandler<(int row, int col, TrayPointStatus status)> NGTrayItemStatusChanged;


        protected NGTray() { }

        public NGTray(int row, int col, double rowDist, double colDist) : base(row, col, rowDist, colDist)
        {
            _items = new NGTrayItem[row * col];
            NGTrayRows = new NGTrayRow[row];
            for (int i = 0; i < row; i++)
            {
                var r = new NGTrayRow(i + 1, col);
                for (int j = 0; j < col; j++)
                {
                    var item = new NGTrayItem(i + 1, j + 1, rowDist, colDist) { Index = i * col + j + 1 };
                    if (!((row == 0 || row == 4) && (col == 3 || col == 4)))
                    {
                        _items[i * col + j] = item;
                    }
                    r.Items[j] = item;
                }
                NGTrayRows[i] = r;
            }

            RowDistance = rowDist;
            ColDistance = colDist;
            //RowDist = rowDist;
            //ColDist = colDist;
        }
        [Obsolete("使用GetNext3")]
        public INGTrayItem? GetNext(int minRow, int maxCol)
        {
            var item = _items?.FirstOrDefault(x => !x.IsExist && x.Row > -minRow && x.Col <= maxCol);
            //item?.SetDistance(RowDist, ColDist);
            item?.SetDistance(RowDistance, ColDistance);
            return item;
        }
        [Obsolete("使用GetNext3")]
        public INGTrayItem? GetNext2(int minRow, int maxCol)
        {
            IEnumerable<NGTrayItem> listItems = new List<NGTrayItem>();
            //TODO：剔除那些不要料盘穴位；临时做法，后期改到界面上 3,6,21,24
            listItems = _items.Where(x => x.Index != 3 && x.Index != 6 && x.Index != 21 && x.Index != 24);
            var item = listItems?.FirstOrDefault(x => !x.IsExist && x.Row > -minRow && x.Col <= maxCol);
            //item?.SetDistance(RowDist, ColDist);
            item?.SetDistance(RowDistance, ColDistance);
            return item;
        }

        public INGTrayItem? GetNext3(int minRow, int maxCol)
        {
            IEnumerable<NGTrayItem> listItems = new List<NGTrayItem>();
            //TODO：剔除那些不要料盘穴位；临时做法，后期改到界面上 3,6,21,24
            listItems = NGTrayRows.SelectMany(x => x.Items);
            listItems = listItems.Where(x => x.Index != 3 && x.Index != 6 && x.Index != 21 && x.Index != 24);
            listItems = listItems.Where(x => x.Status == TrayPointStatus.Empty);
            var item = listItems?.FirstOrDefault(x => !x.IsExist && x.Row > -minRow && x.Col <= maxCol);
            item?.SetDistance(RowDistance, ColDistance);
            return item;
        }
        static NGTray Reload()
        {
            string path = Path.Combine(Environment.CurrentDirectory, "RunningRecord", "NGTray.json");
            path = Path.ChangeExtension(path, ".json");
            if (!File.Exists(path))
            {
                var file = File.Create(path);
                file.Dispose();
            }
            using StreamReader sw = new StreamReader(path);
            var str = sw.ReadToEnd();
            NGTray record = JsonConvert.DeserializeObject<NGTray>(str);
            //record ??= new NGTray(5, 5, 36, 21) { };
            record ??= new NGTray(8, 3, 21, 36) { };
            Instance = record;
            return record;
        }

        public void Save()
        {
            var str = JsonConvert.SerializeObject(this);
            string path = Path.Combine(Environment.CurrentDirectory, "RunningRecord", "NGTray.json");
            path = Path.ChangeExtension(path, ".json");
            if (!File.Exists(path))
            {
                var stream = File.Create(path);
                stream.Close();
            }
            using StreamWriter sw = new StreamWriter(path);
            sw.WriteLine(str);
        }

        public void SetDisable()
        {
            _items.ForEach(x => { x.IsExist = false; x.Status = TrayPointStatus.Empty; });
            Status = NGTrayStatus.Disable;
            Save();
        }

        public void Reset()
        {
            _items.ForEach(x => { x.IsExist = false; x.Status = TrayPointStatus.Empty; });
            Status = NGTrayStatus.Enable;
            Save();
        }

        public void SetFull()
        {
            _items.ForEach(x => { x.IsExist = false; x.Status = TrayPointStatus.CompletedNG; });
            Status = NGTrayStatus.Full;
            Save();
        }

        public void OnNGTrayItemStatusChanged(int row, int col, TrayPointStatus status)
        {
            if (status == TrayPointStatus.NG || status == TrayPointStatus.CompletedNG)
            {
                Status = NGTrayStatus.Full;
            }
            NGTrayItemStatusChanged.SafeInvoke(this, (row, col, status));
        }

        public void OnNGTrayStatusChanged(NGTrayStatus trayStatus)
        {
            NGTrayStatusChanged.SafeInvoke(this, trayStatus);
        }

        public override TrayPointStatus GetTrayPointStatus(int col, int row)
        {
            throw new NotImplementedException();
        }

        public override List<TrayPointStatus> GetTrayPointStatus(int col)
        {
            throw new NotImplementedException();
        }

        public override void SetTrayPointStatus(int col, int row, TrayPointStatus status)
        {
            throw new NotImplementedException();
        }

    }

    public class NGTrayItem : TrayItem, INGTrayItem, INotifyPropertyChanged
    {
        private TrayPointStatus status;

        public NGTrayItem(int row, int col, double rowDistance, double colDistance) : base(row, col, rowDistance, colDistance)
        {
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        public (double PointX, double PointY) GetPutPoint(double baseX, double baseY)
        {
            var (OffsetX, OffSetY) = GetOffsetPoint();
            return (baseX + OffsetX, baseY - OffSetY);
        }

        public void Put()
        {
            IsExist = true;
            Status = TrayPointStatus.CompletedNG;
            NGTray.Instance.Save();
        }
        public TrayPointStatus Status { get => status; set => SetProperty(ref status, value); }

        private bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value))
            {
                return false;
            }
            field = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            return true;
        }
    }

    [AddINotifyPropertyChangedInterface]
    public abstract class Tray
    {
        /// <summary>
        /// 物料盘行数,Y轴方向,入料盘固定为7,NG盘固定为3
        /// </summary>
        public int Row { get; set; } = 7;
        /// <summary>
        /// 物料盘列数,X轴方向,入料盘固定为8,NG盘固定为5
        /// </summary>
        public int Col { get; set; } = 12;
        /// <summary>
        /// 每行的间距,Y轴方向
        /// </summary>
        public double RowDistance { get; set; } = 10;
        /// <summary>
        /// 每列的间距,X轴方向
        /// </summary>
        public double ColDistance { get; set; } = 10;
        /// <summary>
        /// 料盘容量
        /// </summary>
        public virtual int Copacity => Row * Col;
        protected Tray()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="col">物料盘列数,X轴方向,入料盘固定为8,NG盘固定为5</param>
        /// <param name="row">物料盘行数,Y轴方向,入料盘固定为7,NG盘固定为3</param>
        /// <param name="colDist">每列的间距,X轴方向</param>
        /// <param name="rowDist">每行的间距,Y轴方向</param>

        public Tray(int row, int col, double rowDist, double colDist)
        {
            Col = col;
            Row = row;
            RowDistance = rowDist;
            ColDistance = colDist;
        }

        /// <summary>
        /// 根据所在点获取X轴和Y轴坐标偏移,穴位编号顺序先按行(Y方向)计算,再按照列(X方向)计算
        /// </summary>
        /// <param name="index">计数,从1开始算起</param>
        /// <returns></returns>
        public (double offsetX, double offSetY) GetOffsetPoint(int index)
        {
            if (index > Row * Col) throw new Exception($"料盘计数点{index}超过料盘最大数量{Row * Col}");
            int row = index % Row;
            int col = (int)Math.Floor((double)index / (double)Row);
            double offsetx = ColDistance * col;
            double offsety = RowDistance * row;
            return (offsetx, offsety);
        }

        /// <summary>
        /// 获取指定点位状态
        /// </summary>
        /// <param name="col">列数,X轴</param>
        /// <param name="row">行数,Y轴</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public abstract TrayPointStatus GetTrayPointStatus(int col, int row);

        /// <summary>
        /// 获取指列定点位状态
        /// </summary>
        /// <param name="col">列数,X轴</param>
        /// <returns></returns>
        public abstract List<TrayPointStatus> GetTrayPointStatus(int col);

        /// <summary>
        /// 设定指定点位状态
        /// </summary>
        /// <param name="col">列数,X轴</param>
        /// <param name="row">行数,Y轴</param>
        /// <returns></returns>
        public abstract void SetTrayPointStatus(int col, int row, TrayPointStatus status);
    }

    [AddINotifyPropertyChangedInterface]
    public class FeedTray : Tray
    {
        public static FeedTray Instance { get; } = Reload();
        public FeedTrayColunm[] FeedTrayColunms { get; set; }

        public event EventHandler<(int colIndex, int rowIndwx, TrayPointStatus status)> FeedTrayItemStatusChanged;
        private FeedTray(int row, int col, double rowDist, double colDist) : base(row, col, rowDist, colDist)
        {
            FeedTrayColunms = new FeedTrayColunm[col];
            for (int i = 0; i < col; i++)
            {
                FeedTrayColunms[i] = new FeedTrayColunm(i + 1, row, rowDist, colDist);
            }
        }
        protected FeedTray()
        {
        }
        public void Reset()
        {
            foreach (var item in FeedTrayColunms)
            {
                item.Reset();
            }
        }
        static FeedTray Reload()
        {
            string path = Path.Combine(Environment.CurrentDirectory, "RunningRecord", "FeedTray.json");
            path = Path.ChangeExtension(path, ".json");
            if (!File.Exists(path))
            {
                var file = File.Create(path);
                file.Dispose();
            }
            using StreamReader sw = new StreamReader(path);
            var str = sw.ReadToEnd();
            FeedTray record = JsonConvert.DeserializeObject<FeedTray>(str);
            record ??= new FeedTray(7, 12, 34, 38.9) { };
            return record;
        }

        public void Set(int row, int col, double rowdis, double coldis)
        {
            Row = row;
            Col = col;
            RowDistance = rowdis;
            ColDistance = coldis;
        }

        public void Save()
        {
            var str = JsonConvert.SerializeObject(this);
            string path = Path.Combine(Environment.CurrentDirectory, "RunningRecord", "FeedTray.json");
            path = Path.ChangeExtension(path, ".json");
            if (!File.Exists(path))
            {
                var stream = File.Create(path);
                stream.Close();
            }
            using StreamWriter sw = new StreamWriter(path);
            sw.WriteLine(str);
        }

        public override TrayPointStatus GetTrayPointStatus(int col, int row)
        {
            return FeedTrayColunms[col - 1].Items[row - 1].Status;
        }

        public override List<TrayPointStatus> GetTrayPointStatus(int col)
        {
            return FeedTrayColunms[col - 1].Items.Select(x => x.Status).ToList();
        }

        public override void SetTrayPointStatus(int col, int row, TrayPointStatus status)
        {
            FeedTrayColunms[col - 1].Items[row - 1].Status = status;
            FeedTrayItemStatusChanged.SafeInvoke(this, (col, row, status));
        }

        public void SetTrayItem(bool[] hasProducts)
        {
            if (hasProducts.Length != Copacity) throw new Exception($"结果数量{hasProducts.Length}与料盘容量{Copacity}不一致");
            int i = 0;
            foreach (var colunm in FeedTrayColunms)
            {
                foreach (var item in colunm.Items)
                {
                    if (hasProducts[i])
                    {
                        item.Status = TrayPointStatus.OK;
                    }
                    else
                    {
                        item.Status = TrayPointStatus.Empty;
                    }
                    i++;
                }
            }
        }
    }

    public class FeedTrayItem : TrayItem, IFeedTrayItem, INotifyPropertyChanged
    {
        private TrayPointStatus status;
        public FeedTrayItem(int row, int col, double rowDist, double colDist) : base(row, col, rowDist, colDist)
        {
        }
        public event PropertyChangedEventHandler? PropertyChanged;
        public TrayPointStatus Status { get => status; set => SetProperty(ref status, value); }

        private bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value))
            {
                return false;
            }
            field = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            return true;
        }

    }

    [AddINotifyPropertyChangedInterface]
    public class FeedTrayColunm
    {
        public int Index { get; set; }
        public int RowCount { get; set; }
        public FeedTrayItem[] Items { get; set; }
        private FeedTrayColunm() { }
        public FeedTrayColunm(int index, int rowCount, double rowDist, double colDist)
        {
            Index = index;
            RowCount = rowCount;
            Items = new FeedTrayItem[RowCount];
            for (int i = 0; i < rowCount; i++)
            {
                Items[i] = new FeedTrayItem(i + 1, Index, rowDist, colDist) { Index = (index - 1) * rowCount + i + 1 };
            }
        }

        public void Reset()
        {
            foreach (var item in Items)
            {
                item.Status = TrayPointStatus.Empty;
            }
        }
    }

    [AddINotifyPropertyChangedInterface]
    public class NGTrayRow
    {
        public int Index { get; set; }
        public int ColumnCount { get; set; }
        public NGTrayItem[] Items { get; set; }
        private NGTrayRow() { }
        public NGTrayRow(int index, int colCount)
        {
            Index = index;
            ColumnCount = colCount;
            Items = new NGTrayItem[colCount];
        }

    }


    public enum NGTrayStatus
    {
        Disable,
        Enable,
        Full
    }

    public enum TrayPointStatus
    {
        Empty,//无料
        OK,//未撕膜OK料
        NG,//未撕膜NG料
        CompletedOK,//有撕膜完成OK料
        CompletedNG,//有撕膜完成NG料
    }
}
